package com.three.digit.model;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;

import org.apache.commons.lang.StringUtils;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.CategoryToolTipGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.LineAndShapeRenderer;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.ui.RefineryUtilities;

import com.three.digit.tools.Utils;

public class Point
{
    public static String FORMAT_STR = "%1$s     %2$s     %3$s     %4$s";

    private int id;
    private int x;
    private int y;
    private int z;

    public Point previous;
    public Point next;

    public Point (int id, int x, int y, int z)
    {
        this (id + "", x + "" + y + "" + z);
    }

    private static JTabbedPane jTabbedPane = new JTabbedPane ();

    private static HashMap <Integer, Point> map = Utils.toHashMap (Utils.readPoints ("BackUpSanDHistory.txt"));

    public static void main (String[] args)
    {
        Point point = Utils.readPoint ("BackUpSanDHistory.txt");

        final JFrame frame = new JFrame ("3D");
        frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);

        JPanel northJPanel = createOperArea (point);

        final Container root = frame.getContentPane ();
        root.add (jTabbedPane, BorderLayout.CENTER);
        root.add (northJPanel, BorderLayout.NORTH);

        frame.setSize (600, 400);
        RefineryUtilities.centerFrameOnScreen (frame);
        frame.setVisible (true);

    }

    public static JPanel createOperArea (Point point)
    {
        ActionListener actionListener = new ActionListener ()
        {

            public void actionPerformed (ActionEvent e)
            {
                int i = -1;
                if (e.getActionCommand ().equals ("Odd"))
                {
                    i = 0;
                }
                else if (e.getActionCommand ().equals ("Sum"))
                {
                    i = 1;
                }
                else if (e.getActionCommand ().equals ("x"))
                {
                    i = 2;
                }
                else if (e.getActionCommand ().equals ("y"))
                {
                    i = 4;
                }
                else if (e.getActionCommand ().equals ("z"))
                {
                    i = 6;
                }
                if (i >= 0)
                {
                    boolean bool = renderer.getItemVisible (i, 0);
                    renderer.setSeriesVisible (i, Boolean.valueOf (!bool));
                }
                
                if (i >= 2)
                {
                    boolean bool = renderer.getItemVisible (i+1, 0);
                    renderer.setSeriesVisible (i+1, Boolean.valueOf (!bool));
                }

            }
        };
        JCheckBox localJCheckBox5 = new JCheckBox ("Odd");
        localJCheckBox5.setActionCommand ("Odd");
        localJCheckBox5.addActionListener (actionListener);
        localJCheckBox5.setSelected (true);
        JCheckBox localJCheckBox4 = new JCheckBox ("Sum");
        localJCheckBox4.setActionCommand ("Sum");
        localJCheckBox4.addActionListener (actionListener);
        localJCheckBox4.setSelected (true);
        JCheckBox localJCheckBox1 = new JCheckBox ("X");
        localJCheckBox1.setActionCommand ("x");
        localJCheckBox1.addActionListener (actionListener);
        localJCheckBox1.setSelected (true);
        JCheckBox localJCheckBox2 = new JCheckBox ("Y");
        localJCheckBox2.setActionCommand ("y");
        localJCheckBox2.addActionListener (actionListener);
        localJCheckBox2.setSelected (true);
        JCheckBox localJCheckBox3 = new JCheckBox ("Z");
        localJCheckBox3.setActionCommand ("z");
        localJCheckBox3.addActionListener (actionListener);
        localJCheckBox3.setSelected (true);

        JComboBox start = new D3JComboBox (point);
        start.setSize (75, 25);

        JComboBox end = new D3JComboBox (point);
        end.setSize (75, 25);

        JButton jButton = new JButton ("Generate");
        jButton.addActionListener (new D3ActionListener (point, start, end));

        JPanel operContain = new JPanel (new FlowLayout ());

        operContain.add (localJCheckBox5);
//        operContain.add (localJCheckBox4);
        operContain.add (localJCheckBox1);
        operContain.add (localJCheckBox2);
        operContain.add (localJCheckBox3);

        operContain.add (new JLabel ("Start"));
        operContain.add (start);
        operContain.add (new JLabel ("End"));
        operContain.add (end);
        operContain.add (jButton);

        return operContain;
    }

    public static class D3ActionListener implements ActionListener
    {

        private Point point;
        private JComboBox startJomboBox;
        private JComboBox endJomboBox;

        public D3ActionListener (Point point, JComboBox startJomboBox, JComboBox endJomboBox)
        {
            this.point = point;
            this.startJomboBox = startJomboBox;
            this.endJomboBox = endJomboBox;
        }

        public void actionPerformed (ActionEvent e)
        {
            int start = (Integer) startJomboBox.getSelectedItem ();
            int end = (Integer) endJomboBox.getSelectedItem ();

            JButton jButton = (JButton) e.getSource ();
            jButton.setText ("Generate - "
                             + (Math.abs (endJomboBox.getSelectedIndex () - startJomboBox.getSelectedIndex ()) + 1));

            jTabbedPane.removeAll ();
            jTabbedPane.add (createDemoPanel (point, start, end));
            
            for (int i = jButton.getParent ().getComponentCount () - 1; i >= 0; i--)
            {
                Component component = jButton.getParent ().getComponent (i);
                if (component instanceof JCheckBox)
                {
                    ((JCheckBox)component).setSelected (true);
                }
            }
        }
    }

    public static JPanel createDemoPanel (Point point, int start, int end)
    {
        JFreeChart localJFreeChart = createChart (createDataset (point, start, end));
        JPanel jPanel = new ChartPanel (localJFreeChart);
        jPanel.setName ("chart");
        return jPanel;
    }

    private static CategoryDataset createDataset (Point point, int start, int end)
    {
        DefaultCategoryDataset dcd = new DefaultCategoryDataset ();

        do
        {
            if (point.getId () >= start)
            {
                int Odd = point.getX () % 2 + point.getY () % 2 + point.getZ () % 2;
                dcd.addValue (Odd, "Odd", point.getId () + "");
                dcd.addValue (point.sum (), "Sum", point.getId () + "");
                dcd.addValue (point.getX (), "X", point.getId () + "");
                dcd.addValue ((2 - point.getX () % 2), "X-odd", point.getId () + "");
                dcd.addValue (point.getY (), "Y", point.getId () + "");
                dcd.addValue ((2 - point.getY () % 2), "Y-odd", point.getId () + "");
                dcd.addValue (point.getZ (), "Z", point.getId () + "");
                dcd.addValue ((2 - point.getZ () % 2), "Z-odd", point.getId () + "");
            }
        }
        while (((point = point.next) != null && point.getId () <= end));

        return dcd;
    }

    private static LineAndShapeRenderer renderer;

    private static JFreeChart createChart (CategoryDataset paramXYDataset)
    {
        JFreeChart jFreeChart = ChartFactory.createLineChart (null, "Year", "Value", paramXYDataset,
                                                              PlotOrientation.VERTICAL, true, true, false);

        CategoryPlot categoryPlot = (CategoryPlot) jFreeChart.getPlot ();
        
        categoryPlot.setDomainCrosshairVisible(true);
        categoryPlot.setRangeCrosshairVisible(true);

        NumberAxis numberAxis = (NumberAxis) categoryPlot.getRangeAxis ();
        numberAxis.setStandardTickUnits (NumberAxis.createIntegerTickUnits ());

        CategoryAxis categoryAxis = categoryPlot.getDomainAxis ();
        categoryAxis.setCategoryLabelPositions (CategoryLabelPositions.UP_45);

        renderer = (LineAndShapeRenderer) categoryPlot.getRenderer ();

        renderer.setBaseToolTipGenerator (new CategoryToolTipGenerator ()
        {

            public String generateToolTip (CategoryDataset dataset, int row, int column)
            {

                Point p = map.get (Integer.valueOf (dataset.getColumnKey (column).toString ()));

                return dataset.getColumnKey (column) + " " + p.getPresent () + " " + dataset.getRowKey (row) + " = "
                       + dataset.getValue (row, column).intValue ();
            }
        });

        renderer.setBaseShapesVisible (true);
        renderer.setDrawOutlines (true);
        renderer.setUseFillPaint (true);
        renderer.setBaseFillPaint (Color.white);

        return jFreeChart;
    }

    public Point (int id, int value)
    {
        this (id + "", value + "");
    }

    public Point (String id, String x, String y, String z)
    {
        this (id, x.concat (y).concat (z));
    }

    public Point (String id, String value)
    {
        this.id = Integer.valueOf (id);
        value = StringUtils.leftPad (value, 3, "0");
        this.x = Integer.valueOf (value.substring (0, 1));
        this.y = Integer.valueOf (value.substring (1, 2));
        this.z = Integer.valueOf (value.substring (2));
    }

    public int sum ()
    {
        return x + y + z;
    }

    public int max ()
    {
        return Math.max (Math.max (x, y), z);
    }

    public int min ()
    {
        return Math.min (Math.min (x, y), z);
    }

    public int span ()
    {
        return max () - min ();
    }

    /**
     * If Point A is similar with Point B, which means that they have the same
     * value of sum, max and min.
     * 
     * @param p
     * @return
     */
    public boolean similar (Point p)
    {
        return p.sum () == this.sum () && p.max () == this.max () && p.min () == this.min ();
    }

    public boolean equal (Point p)
    {
        return this.getValue () == p.getValue ();
    }

    @Override
    public String toString ()
    {
        return String.format (FORMAT_STR, id, x, y, z);
    }

    public String toString (String format)
    {
        return String.format (format, id, x, y, z);
    }

    public int getId ()
    {
        return id;
    }

    public void setId (int id)
    {
        this.id = id;
    }

    public int getX ()
    {
        return x;
    }

    public void setX (int x)
    {
        this.x = x;
    }

    public int getY ()
    {
        return y;
    }

    public void setY (int y)
    {
        this.y = y;
    }

    public int getZ ()
    {
        return z;
    }

    public void setZ (int z)
    {
        this.z = z;
    }

    public int getValue ()
    {
        return Integer.valueOf (getPresent ());
    }

    public void setValue (int v)
    {
        String value = v + "";
        value = StringUtils.leftPad (value, 3, "0");
        this.x = Integer.valueOf (value.substring (0, 1));
        this.y = Integer.valueOf (value.substring (1, 2));
        this.z = Integer.valueOf (value.substring (2));
    }

    public String getPresent ()
    {
        return x + "" + y + "" + z;
    }

    public boolean hasPrevious ()
    {
        return previous != null;
    }

    public Point previous ()
    {
        return previous;
    }

    public void setPrevious (Point previous)
    {
        this.previous = previous;
    }

    public boolean hasNext ()
    {
        return next != null;
    }

    public Point next ()
    {
        return next;
    }

    public void setNext (Point next)
    {
        this.next = next;
    }
}
